<?php
/**
 * 
 * @todo description of this class
 * 
 * @author Alton Crossley <crossleyframework@nogahidebootstrap.com>
 * @package Crossley Framework
 *  
 * @copyright Copyright (c) 2003-2009, Nogaide BootStrap INC. All rights reserved.
 * @license BSD http://opensource.org/licenses/bsd-license.php
 * @version $Id:$
 * 
 */
/**
 * this is a simple transport device for a HTTP Controller event
 * 
 * It offers the functionality of tracking the path prosession as well 
 * as where the controller is and request info
 *
 */
class X_Broker_Event implements X_Broker_Event_Interface
{
    /**
     * user object
     * @var X_User_Interface
     */
    private $_oUser;
    /**
     * full path of the current running script
     * including appended path
     * 
     * Answers "Where are we currently at in
     * relation to domain root?"
     *
     * @var string
     */
    private $_sFullPath;
    /**
     * current query string
     *
     * @var string
     */
    private $_sUrlQuery;
    /**
     * base url with appended path removed
     * 
     * Answers "Where is the controller?"
     *
     * @var string
     */
    private $_sBaseUrl;
    /**
     * array of path crums to be processed
     *
     * @var array
     */
    private $_aPath;
    /**
     * array of processed path crums
     *
     * @var array
     */
    private $_aHistory = array();
    /**
     * array of submited data
     *
     * @var array
     */
    private $_aData;
    /**
     * unfiltered values
     *
     * @var array
     */
    private $_aRawData;
    
    /**
     * unique id for this event based on data and request
     *
     * @var string
     */
    private $_sUid;
    
    /**
     * @return array
     */
    public function getHistory()
    {
        return $this->_aHistory;
    }
	/**
     * @param X_User_Interface $_oUser user to set
     */
    public function setUser($_oUser)
    {
        $this->_oUser = $_oUser;
    }

	/**
     * @return X_User_Interface
     */
    public function getUser()
    {
        return $this->_oUser;
    }

    /**
     * get the equivelent of ../ on the url
     *
     * @param int $iCount number of directories to traverse
     * @return unknown
     */
    public function getParentUrl($iCount = null)
    {
        $iCount = (int)$iCount;
        $aHistory = $this->getHistory();
        
        if (!$iCount)
        {
            $iCount = count($aHistory)-1;
        }
        
        $aParentHistory = array();
        for ($c = 0; $c < $iCount; $c++)
        {
            if (array_key_exists($c, $aHistory))
                $aParentHistory[] = $aHistory[$c];
        }
        
        return '/'.implode('/', $aParentHistory);
    }
    
    /**
     * @return string
     */
    public function getBaseUrl()
    {
        return $this->_sBaseUrl;
    }
    
    /**
     * @return string
     */
    public function getFullPath()
    {
        return $this->_sFullPath;
    }
    
    /**
     * @return string
     */
    public function getUrlQuery()
    {
        return $this->_sUrlQuery;
    }
    
    /**
     * @param array $aData
     */
    public function setData(Array $aData, $bMerge = false)
    {
        if ($bMerge)
        {
            $this->_aRawData = array_merge($this->_aRawData, $aData);
        }
        else 
        {
            $this->_aRawData = $aData;
        }
        
        $this->_aData = X_Filter_Alnum::go($this->_aRawData, true, array('_', '-', ' '));
    }
    
    /**
     * @param array $aHistory
     */
    public function setHistory($aHistory)
    {
        $this->aHistory = $aHistory;
    }
    
    public function stSetBaseUrl($sBaseUrl = null)
    {
        if (empty($sBaseUrl))
        {
            $this->_sBaseUrl = parse_url(str_replace($this->_sFullPath, '', $_SERVER['PHP_SELF']));
            $this->_sBaseUrl = $this->_sBaseUrl['path'];
        }
        else
        {
            $this->_sBaseUrl = $sBaseUrl;
        }
    }
    
    /**
     * @param string $_sFullPath
     */
    public function setFullPath($sFullPath)
    {
        $this->_sFullPath = $sFullPath;
    }
    
    /**
     * @param string $_sUrlQuery
     */
    public function setUrlQuery($sUrlQuery)
    {
        $this->_sUrlQuery = $sUrlQuery;
    }
    
    private function _setData()
    {
        if (! isset($this->_aData))
        {
        	
        	$aSecondary = X_Url::getSecondaryValues();
        	
            /**
             * @todo add filtering to GPR data
             */
            $this->setData(array_merge($_GET, $_POST, $aSecondary));
        }
    }
    
    /**
     * @return array
     */
    public function getData($sKey = null)
    {
        $this->_setData();
        
        if ($sKey)
        {
            if (array_key_exists($sKey, $this->_aData))
            {
                return $this->_aData[$sKey];
            }
            
            return false;
        }
        
        return $this->_aData;
    }
    
    public function getRawData($sKey = null)
    {
        $this->_setData();
        
        if ($sKey)
        {
            if (array_key_exists($sKey, $this->_aRawData))
            {
                return $this->_aRawData[$sKey];
            }
            
            return false;
        }
        
        return $this->_aRawData;
    }
    
    public function getHtmlEncoded($sKey = null)
    {
        $this->_setData();
        
        if ($sKey)
        {
            if (array_key_exists($sKey, $this->_aRawData))
            {
                return X_Filter_HtmlEntities::go($this->_aRawData[$sKey]);
            }
            
            return false;
        }
        
        return X_Filter_HtmlEntities::go($this->_aRawData);
    }
    
    /**
     * returns the path beyond the controller, and returns 
     * the crumbs in an array
     * 
     * @param bool $bRefresh force refresh
     * @return array
     */
    public function getPath($bRefresh = false)
    {
        if ($this->_aPath && ! $bRefresh)
        {
            return $this->_aPath;
        }
        
        $this->_resetPathArray(trim($this->getPathInfo()));
        
        return $this->_aPath;
    }
    
    /**
     * set the internal path array and reset history array
     *
     * @param string $sPathInfo
     */
    private function _resetPathArray($sPathInfo)
    {
        $this->_aPath = array();
        $this->_aHistory = array();
        
        if (empty($sPathInfo))
        {
            $this->_aPath = array('index' , 'index');
        } else
        {
            $this->_aPath = explode('/', $sPathInfo);
            if (empty($this->_aPath[0]))
            {
                unset($this->_aPath[0]);
            }
        }
        
        if (count($this->_aPath) == 1)
        {
            array_push($this->_aPath, 'index');
        }
        
        $this->_aPath = X_Filter_Alnum::go($this->_aPath, true, array('.', '_', '-', ' '));
    }
    
    /**
     * @param array $_aPath
     */
    public function setPath(Array $xPath = null)
    {
        if (empty($xPath))
        {
            // if we passed nothing in, we need to set it
            $this->getPath(true);
        } else if (is_array($xPath))
        {
            $this->_aPath = $xPath;
        } else
        {
            $this->_aPath = explode('/', (string) $xPath);
        }
    }
    /**
     * move path position back to the begining
     *
     */
    public function resetPath()
    {
        $this->_aPath = array_merge($this->_aHistory, $this->_aPath);
    }
    
    /**
     * return an instance of the current class
     * /MAY/ be an singelton, but not a requirement
     *
     * @return __CLASS__
     */
    static public function getInstance()
    {
        $sMyClass = __CLASS__;
        $oInstance = new $sMyClass();
        return $oInstance;
    }
    
    /**
     * obtains the path from the $_ENV or $_SERVER predefined arrays
     * 
     * responsible for setting $_sBaseUrl, $_sUrlQuery, $_sFullPath
     * 
     * @todo clean getPathInfo() up
     *
     * @return string|false if not set in either array
     */
    public function getPathInfo($sPath = null)
    {
        $sPathInfo = '';
        $aParseUrl = array();
        
        if (empty($sPath))
        {
            if (array_key_exists('PATH_INFO', $_ENV) && ! empty($_ENV['PATH_INFO']))
            {
                $sPathInfo = $_ENV['PATH_INFO'];
            } 
            elseif (array_key_exists('PATH_INFO', $_SERVER) && ! empty($_SERVER['PATH_INFO']))
            {
                $sPathInfo = $_SERVER['PATH_INFO'];
            } 
            print X_Debug::out($_SERVER);
            if (array_key_exists('REQUEST_URI', $_SERVER))
            {
                $aParseUrl = $this->_parseUrl($_SERVER['REQUEST_URI']);
            } 
            else if (array_key_exists('SCRIPT_URL', $_SERVER))
            {
                $aParseUrl = $this->_parseUrl($_SERVER['SCRIPT_URL']);
            }
        }
        else 
        {
            $sPathInfo = $sPath;
            $aParseUrl = $this->_parseUrl($sPath);
        }
        
        if (array_key_exists('query', $aParseUrl))
        {
            $this->_sUrlQuery = $aParseUrl['query'];
        }
        
        $this->_sFullPath = $aParseUrl['path'];
        
        // last ditch effort to set the path info
        if (empty($sPathInfo))
        {
            $sPathInfo = str_replace(dirname($_SERVER["SCRIPT_NAME"]), '', $this->_sFullPath);
            $sPathInfo = str_replace('//', '/', str_replace(basename($_SERVER["SCRIPT_NAME"]), '', $sPathInfo));
        }
        
        if (empty($this->_sBaseUrl))
        {
            $this->stSetBaseUrl();
        }
        
        if (strlen(trim($sPathInfo)) > 1)
        {
            return $sPathInfo;
        }
        
        return false;
    }
    
    private function _parseUrl($sUrl)
    {
    	$aMatches = array();
    	//$iMatchCount = preg_match_all('/?P<name>.*?\?/', $sUrl, $aMatches);
    	$iMatchCount = preg_match('|^(?=[^&])(?:(?<scheme>[^:/?#]+):)?(?://(?<authority>[^/?#]*))?(?<path>[^?#]*)(?:\?(?<query>[^#]*))?(?:#(?<fragment>.*))?|', $sUrl, $aMatches);
    	return $aMatches;
    }
   
    
    function __construct($bAutoBuild = true)
    {
        // @todo handle soap calls in addition to REST (maybe seperate event?)
        // @todo handle authentication token lookup
        if ($bAutoBuild)
        {
            $this->getPath();
            $this->getData();
        }

        // set default user
        $this->setUser(new X_User());
    }
    
    function setUri($sUri)
    {
        $sPath = $this->getPathInfo($sUri);
        $this->_resetPathArray(trim($sPath));
    }
    
    /**
     * move a path event from path to history
     *
     * @param array $aEvent
     * @return string
     */
    public function getStep()
    {
        if (empty($this->_aPath))
        {
            return false;
        }
        
        if (is_array($this->_aPath))
        {
            $sStep = array_shift($this->_aPath);
            array_push($this->_aHistory, $sStep);
            
            return $sStep;
        }
        
        X_Debug::formatHtml(null, '$this->_aPath is not an array');
        
        return false;
    }
    /**
     * @see X_Broker_Event_Interface::getUid()
     *
     */
    public function getUid($bRebuild = false)
    {
        if (isset($this->_sUid) && !$bRebuild) return $this->_sUid;
        
        $this->_sUid = md5(var_export($this->_aData, true) . $this->_sFullPath);
        
        return $this->_sUid;
    }
    
    public function getDebug()
    {
        $aReturn = array();
        
        $aReturn['base_url'] = $this->_sBaseUrl;
        $aReturn['url_query'] = $this->_sUrlQuery;
        $aReturn['full_path'] = $this->_sFullPath;
        
        $aReturn['data'] = $this->_aData;
        $aReturn['history'] = $this->_aHistory;
        $aReturn['path'] = $this->_aPath;
        
        $aReturn['parent_url'] = $this->getParentUrl();
        $aReturn['uid'] = $this->_sUid;
        
        return $aReturn;
    }

}